#pragma once
#include "db.hpp"
#include "online.hpp"
#include "util.hpp"

#define BOARD_ROW 15
#define BOARD_COL 15
#define CHESS_WHITE 1
#define CHESS_BLACK 2

typedef enum { 
    GAME_START, 
    GAME_OVER 
} room_status;

class room
{
public:
    room(uint64_t room_id, user_table* tb_user, online_manager* online_user):
        _room_id(room_id), _status(GAME_START), _play_count(0),
        _tb_user(tb_user), _online_user(online_user), 
        _board(BOARD_ROW, std::vector<int>(BOARD_COL, 0)) { 
        DLOG("%lu 房间创建成功", _room_id); 
    }
    ~room() { 
        DLOG("%lu 房间销毁成功", _room_id); 
    }

    uint64_t id() { return _room_id; }
    room_status status() { return _status; }
    int player_count() { return _play_count; }
    void add_white_user(uint64_t uid) { _white_id = uid; _play_count++; }
    void add_black_user(uint64_t uid) { _black_id = uid; _play_count++; }
    uint64_t get_white_user() { return _white_id; }
    uint64_t get_black_user() { return _black_id; }

    // 处理下棋动作
    Json::Value handle_chess(Json::Value& requset)
    {
        // 1. 判断房间中两个玩家是否都在线，任意一个玩家不在线则另一个玩家胜利
        Json::Value response = requset;
        int chess_row = requset["row"].asInt();
        int chess_col = requset["col"].asInt();
        uint64_t cur_uid = requset["uid"].asUInt64();
        if (_online_user->is_in_game_room(_white_id) == false) {
            response["result"] = true;
            response["reason"] = "对方掉线，不战而胜";
            response["winner"] = _black_id;
            return response;
        }
        if (_online_user->is_in_game_room(_black_id) == false) {
            response["result"] = true;
            response["reason"] = "对方掉线，不战而胜";
            response["winner"] = _white_id;
            return response;
        }
        // 2. 获取走棋位置，判断当前走棋是否合理（位置是否被占用）
        if (_board[chess_row][chess_col] != 0) {
            response["result"] = false;
            response["reason"] = "当前位置已经被棋子占用";
            return response;
        }
        int cur_color = cur_uid == _white_id ? CHESS_WHITE : CHESS_BLACK; // 当前棋子的颜色
        _board[chess_row][chess_col] = cur_color;
        // 3. 判断是否有玩家胜利（从当前走棋位置开始判断是否存在五星连珠）
        uint64_t winner_id = check_win(chess_row, chess_col, cur_color);
        if (winner_id != 0) {
            response["reason"] = "五星连珠，战无敌";
        }
        response["result"] = true;
        response["winner"] = winner_id;
        return response;
    }
    // 处理聊天动作
    Json::Value handle_chat(Json::Value& requset)
    {
        // 检测消息中是否包含敏感词
        Json::Value response = requset;
        std::string msg = requset["messge"].asString();
        size_t pos = msg.find("垃圾");
        if (pos != std::string::npos) {
            response["result"] = false;
            response["reason"] = "消息中包含敏感词汇，无法发送";
            return response;
        }
        response["result"] = true;
        return response;
    }
    // 处理玩家退出房间动作，连接断开之后被调用
    void handle_exit(uint64_t uid)
    {
        // 1. 如果是下棋当中退出，则对方胜利；如果是下棋结束后退出，则是正常退出
        Json::Value response;
        if (_status == GAME_START) {
            uint64_t winner_id = uid == _white_id ? _black_id : _white_id;
            uint64_t lose_id = winner_id == _white_id ? _black_id : _white_id;
            response["optype"] = "put_chess";
            response["result"] = true;
            response["reason"] = "对方掉线，不战而胜";
            response["room_id"] = _room_id;
            response["uid"] = uid;
            response["row"] = -1;
            response["col"] = -1;
            response["winner"] = winner_id;
            _tb_user->win(winner_id);
            _tb_user->lose(lose_id);
            _status = GAME_OVER;
            broadcast(response);
        }
        // 2. 房间玩家数量--
        _play_count--;
    }
    // 总的处理请求函数，区分请求类型，根据不同的请求调用不同的处理函数，得到响应进行广播
    void handle_requset(Json::Value& requset)
    {
        // 1. 校验房间号是否匹配
        Json::Value response;
        uint64_t room_id = requset["room_id"].asUInt64();
        if (room_id != _room_id) {
            response["optype"] = requset["optype"].asString();
            response["result"] = false;
            response["reason"] = "房间号不匹配";
            return broadcast(response);
        }
        // 2. 根据不同的请求调用不同的处理函数
        if (requset["optype"].asString() == "put_chess") {
            response = handle_chess(requset);
            if (response["winner"].asUInt64() != 0) {
                uint64_t winner_id = response["winner"].asUInt64();
                uint64_t loser_id = winner_id == _white_id ? _black_id : _white_id;
                _tb_user->win(winner_id);
                _tb_user->lose(loser_id);
                _status = GAME_OVER;
            }
        } 
        else if (requset["optype"].asString() == "chat") {
            response = handle_chat(requset);
        }
        else {
            response["optype"] = requset["optype"].asString();
            response["result"] = false;
            response["reason"] = "未知请求类型";
        }
        // 3. 广播消息
        std::string body;
        json_util::serialize(response, body);
        DLOG("房间-广播动作：%s", body.c_str());
        return broadcast(response);
    }
    // 将指定的信息广播给房间中所有玩家
    void broadcast(Json::Value& response)
    {
        // 1. 对要响应的信息进行序列化，将Json::Value中的数据序列化为json格式的字符串
        std::string body;
        json_util::serialize(response, body);
        // 2. 获取房间中所有用户的通信连接，并发送响应信息
        wsserver_t::connection_ptr white_conn = _online_user->get_conn_from_room(_white_id);
        if (white_conn.get() != nullptr) {
            white_conn->send(body);
        } else {
            DLOG("房间-白棋玩家连接获取失败");
        }
        wsserver_t::connection_ptr black_conn = _online_user->get_conn_from_room(_black_id);
        if (black_conn.get() != nullptr) {
            black_conn->send(body);
        } else {
            DLOG("房间-黑棋玩家连接获取失败");
        }
    }

private:
    // 判断下棋的位置是否达成五星连珠
    bool five(int row, int col, int row_off, int col_off, int color)
    {
        // row和col是下棋位置，row_off和col_off是偏移量，也是方向
        int count = 1;
        int serach_row = row + row_off;
        int serach_col = col + col_off;
        while (serach_row >= 0 && serach_row <= BOARD_ROW
            && serach_col >= 0 && serach_col <= BOARD_COL
            && _board[serach_row][serach_col] == color)
        {
            // 同色棋子数量++
            count++;
            // 继续向后检索棋子
            serach_row += row_off;
            serach_col += col_off;
        }
        serach_row = row - row_off;
        serach_col = col - col_off;
        while (serach_row >= 0 && serach_row <= BOARD_ROW
            && serach_col >= 0 && serach_col <= BOARD_COL
            && _board[serach_row][serach_col] == color)
        {
            // 同色棋子数量++
            count++;
            // 继续向后检索棋子
            serach_row -= row_off;
            serach_col -= col_off;
        }
        if (count >= 5) {
            return true;
        }
        return false;
    }
    // 若有玩家胜利返回胜利玩家的id，否则返回0
    uint64_t check_win(int row, int col, int color)
    {
        // 从下棋位置的四个方向检测是否出现5个及以上相同颜色的棋子（横行、纵列、正斜、反斜）
        if (five(row, col, 0, 1, color) || five(row, col, 1, 0, color) || 
            five(row, col, -1, 1, color) || five(row, col, -1, -1, color)) {
            // 任意一个方向上出现了五星连珠说明该玩家胜利
            return color == CHESS_WHITE ? _white_id : _black_id;
        }
        return 0;
    }

private:
    uint64_t _room_id;    // 房间号
    room_status _status;  // 房间状态
    int _play_count;      // 玩家数量
    uint64_t _white_id;   // 白棋玩家号
    uint64_t _black_id;   // 黑棋玩家号
    user_table* _tb_user; // 玩家积分表
    online_manager* _online_user; // 在线用户管理
    std::vector<std::vector<int>> _board; // 棋盘
};

using room_ptr = std::shared_ptr<room>;
class room_manager
{
public:
    room_manager(user_table* ut, online_manager* om):
        _next_rid(1), _tb_user(ut), _online_user(om) {
        DLOG("房间管理模块初始化完毕");
    }
    ~room_manager() {
        DLOG("房间管理模块销毁完毕");
    }

    // 为两个用户创建房间，并返回房间的智能指针管理对象
    room_ptr create_room(uint64_t uid1, uint64_t uid2)
    {
        // 两个用户在游戏大厅中进行对战匹配，匹配成功后创建房间
        // 1. 校验两个用户是否还在游戏房间，只有都在才需要创建房间
        if (_online_user->is_in_game_hall(uid1) == false) {
            DLOG("用户：%lu 不在游戏大厅中，创建房间失败", uid1);
            return room_ptr();
        }
        if (_online_user->is_in_game_hall(uid2) == false) {
            DLOG("用户：%lu 不在游戏大厅中，创建房间失败", uid2);
            return room_ptr();
        }
        // 2. 创建房间，并将用户信息添加到房间中
        std::unique_lock<std::mutex> lock(_mutex);
        room_ptr rp(new room(_next_rid, _tb_user, _online_user));
        rp->add_white_user(uid1);
        rp->add_black_user(uid2);
        // 3. 将房间信息管理起来
        _rooms.insert(std::make_pair(_next_rid, rp));
        _users.insert(std::make_pair(uid1, _next_rid));
        _users.insert(std::make_pair(uid2, _next_rid));
        _next_rid++; // 房间id++
        // 4. 返回房间信息
        return rp;
    }
    // 通过房间id获取房间信息
    room_ptr get_room_by_rid(uint64_t rid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _rooms.find(rid);
        if (it == _rooms.end()) {
            return room_ptr();
        }
        return it->second;
    }
    // 通过用户id获取房间信息
    room_ptr get_room_by_uid(uint64_t uid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        // 1. 通过用户id获取房间id
        auto uit = _users.find(uid);
        if (uit == _users.end()) {
            return room_ptr();
        }
        uint64_t rid = uit->second;
        // 2. 通过房间id获取房间信息（注意：不能复用get_room_by_rid，否则造成死锁）
        auto rit = _rooms.find(rid);
        if (rit == _rooms.end()) {
            return room_ptr();
        }
        return rit->second;
    }
    // 通过房间id销毁房间
    void remove_room(uint64_t rid)
    {
        // 因为房间信息是通过shared_ptr在_rooms中进行管理，因此只要将shared_ptr从_rooms中移除
        // 则shared_ptr计数器等于0，外界没有对房间信息进行操作保存的情况下就会释放
        // 1. 通过房间id获取房间信息
        room_ptr rp = get_room_by_rid(rid);
        if (rp.get() == nullptr) {
            return;
        }
        // 2. 通过房间信息，获取房间中所有用户id
        uint64_t uid1 = rp->get_white_user();
        uint64_t uid2 = rp->get_black_user();
        // 3. 移除房间管理中的用户信息
        std::unique_lock<std::mutex> lock(_mutex);
        _users.erase(uid1);
        _users.erase(uid2);
        // 4. 移除房间管理信息
        _rooms.erase(rid);
    }
    // 删除房间中指定的用户，如果房间中没有用户了，则销毁房间，用户连接断开时被调用
    void remove_room_user(uint64_t uid)
    {
        // 1. 通过房间id获取房间信息
        room_ptr rp = get_room_by_uid(uid);
        if (rp.get() == nullptr) {
            return;
        }
        // 2. 处理玩家退出操作
        rp->handle_exit(uid);
        // 3. 房间中没有玩家了，则销毁房间
        if (rp->player_count() == 0) {
            remove_room(rp->id());
        }
    }

private:
    uint64_t _next_rid; // 下个房间的id
    user_table* _tb_user; // 玩家积分表
    online_manager* _online_user; // 在线用户管理
    std::mutex _mutex; // 互斥锁保证哈希表的线程安全
    std::unordered_map<uint64_t, uint64_t> _users; // 用户id映射房间id
    std::unordered_map<uint64_t, room_ptr> _rooms; // 房间id映射房间信息
};